由于JFR是基于事件进行记录的,因此在实现上与JRA有很大区别。例如,在JFR中,几乎所有的标签页都有范围选择器。因为是基于事件记录的,所以可以通过指定时间范围进行过滤,而且数据的粒度可以更细,数据源也更多。
下面会对其中的区别做详细介绍。
在下面的截图中展示了JFR中 通用 | 概览标签页中的内容。与JRA中相比(参见第8章中的内容),略有不同。
正如前面提到的,在JFR中,几乎所有的标签页都有范围选择器。范围选择器的背景图案显示了在记录时间范围内所发生的事件数量。例如,在 概览标签页内,包含有堆、垃圾回收、CPU使用率和通用信息事件。
使用基于事件的数据模型所带来的影响是,可以通过范围选择器来选取出有数据但没事件的时间区间。在第8章中曾介绍过,通用信息是写在记录尾部的。在JFR中,某些事件会写到记录块的尾部。如果选取的范围中并没有期望的时间,则会显示 N/A,如下图所示。因此,在修改选择范围时,应该注意是否还包含有期望的事件。
由于范围选择器在很多地方都会用到,因此需要在响应的地方进行同步。勾选 Synchronize Selection复选框,确保其他使用了范围选择的标签页可以同步修改。
与JRA中的情况类似,修改了 操作集(Operative Set)之后,会突出显示。
相比于JRA,操作集有所增强。现在可以在绝大部分视图中修改操作集,即便是模拟JRA风格的标签页也是如此。
在上面的截图中,通过代码采样数据可以看出javax.swing
包中的代码最热。响应的事件在范围选择器中也高亮显示了。
每个事件属性都有一个关联键,全局唯一,用于与其他事件类型相区别。例如,垃圾回收事件的关联键是GCID,格式为 http://www.oracle.com/jrockit/jvm/vm/gc/id。通过GCID就可以方便的找出所有与指定垃圾回收相关的所有事件。
关联键采用了URI的格式,类似于XML文件中的命名空间。
在用户界面中,可以通过上下文菜单将关联键添加 操作集中,如下图所示:
第三方事件生产者可以在多个生产者之间使用关联键来标识某个事件。例如,WLDS和DMS使用关联键ECID(Execution Context ID)在WebLogic Server的探针和生产者之间标识相应事件,又或者将所有与某个数据库调用相关的事件都添加到操作集当中。
生产者也可以为其他属性提供关联键。在下面的截图中,使用试验性的WebLogic Tab Pack将匹配SQL语句的事件添加到操作集中。
本书中示例中所使用的一些试验性插件(例如WebLogic Tab Pack)只适用于JRockit Misson Control 4.0.1及其后的版本。本书可能会在JRockit Mission Control 4.0.1版本之前发布,因此会造成插件无法使用。不过好消息是,在JRockit Mission Control 4.0.1中可以直接下载这些试验性的插件。希望读者届时尽快升级Mission Control。
一切皆事件,因此不必像JRA那样假设"所有的事件都会有相对延迟"。"相对延迟",是指事件会在执行线程中逐个执行。在JFR中,有专门的标签页来处理具有相对延迟的事件,而那些具有相对延迟的Java事件仍需要以类似JRA延迟分析器那种方式来处理。
事件标签组中包含了用于事件可视化的通用标签组,类似于JRA中的延迟标签组。如下图所示:
如果在 事件类型视图中,只选择 Java应用程序事件,则结果会与JRA延迟分析器中的结果类似。
在 CPU | 线程标签组中有专门与延迟相关的标签页。在JFR中,增加了一些新的标签页,用于汇总相对延迟事件。例如, 在延迟标签页对事件类型做了汇总,并为选中的事件类型显示调用栈信息。下面的示例是对Java 2D示例应用程序进行记录的结果,如图所示,大部分线程都处于睡眠状态。
上图中的结果符合预期,因为大部分线程都会在渲染和休眠之间转换。值得注意的是,饼图左侧的数据是基于事件数量的,而不是基于最大持续时间,表格中的数据也是基于事件数量进行排序的。
在Mission Control 4.0.0中,饼图是被绑定与某个预定义表格列的。使用设计模式(design mode)可以修改表格列的内容。在JFR后续的小版本中,会添加参数来将饼图与最后一次排序的数值字段做绑定。
JFR中引入的 竞争标签页用于展示Java阻塞事件(线程阻塞,等待获取监视器)。在下面的截图中,展示的是WebLogic Server中的事件记录,从中可以看到,在调用loadWebApp
方法时,需要获取weblogic.wervlet.internal.HttpServer
示例的监视器,这个过程所消耗的时间最多。
在 竞争标签页中,还可以看到,哪些线程最容易被阻塞,哪些线程最容易阻塞其他线程。
与JRA类似,可以在JFR中启用详细的锁分析(lock profiling),当然,开启之后,不论是否在执行分析,都会给运行时带来额外的负担。启动JRockit时,添加命令行参数-XX:+UseLockProfiling
以启用锁分析。更多详细信息,请参见第4章内容。
在JRA中,只能统计被抛出的异常进行数量统计。若想对代码中抛出并捕获的异常进行统计,就不得不修改JVM的日志设置,例如,通过JRCMD或在JVM的启动参数中添加-Xverbose:exceptions=debug
选项。就JFR来说,有专门的事件类型和必要的信息来记录异常事件,便于查看。在创建记录的时候,使用包含异常分析的模板即可。
上图中的示例,是运行Java 2D示例应用程序所做的记录,从中可以看到,大部分InterruptedException
异常都由java2d.AnimatingSurface.run()
方法抛出。范围选择器中显示,有两个抛出异常的峰值,分别在记录的开始和结束时。记录开始时大量抛出的是ClassCastException
异常,而在结束时抛出的主要是NoSuchMethodException
异常。通过范围选择器将时间范围限定到峰值附件可以很清楚的看到这一点。如下图所示:
Java 2D示例应用程序并不会抛出太多异常,因此只能用来介绍如何使用相关用户界面。在实际场景中,如果应用程序抛出的异常太多,就应该详查原因了。
尽管JFR中的内存分析标签页与JRA非常相似,还是有一些事情需要注意一下。
在 内存 | 概览标签页中,Total Physical Momery和 Used Physical Memory数值均指向当前机器的物理内存,而不是Java进程的内存。在之前的Java 2D示例应用程序记录中,应用程序是在一台具有4GB内存机器上运行的,在开始记录的时候,已经使用了大约2.2GB内存。Java堆申请的内存只有128MB。这些数据可以在内存标签页的展示图或 垃圾回收配置中看到。在 堆容量标签页可以看到相关展示图。
在 堆容量标签页中包含了两个展示图,一个用于展示堆中容量,另一个用于展示堆中空闲空间的分布。
在 空闲内存分布图(Free Memory Distribution)中,灰色图案表示已使用的内存数量,病区分出已使用的部分,和碎片部分。示例图所显示的堆内存处于非常良好的状况,空闲内存很多,碎片化程度较低,连续的空闲空间很大。如果堆中满是小的空闲快,则无法为较大的数组分配内存,从而导致垃圾回收,执行耗时的内存整理操作,甚至暂停应用程序的运行。
下面的截图来自于一个有内存泄露的示例应用程序,在第10章中会对其做进一步介绍。如图所示,存货对象集的大小随时间不断增长,而堆中的空闲内存块则被不断的切分: